package com.googlecode.likesql.context;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.googlecode.likesql.constant.SqlChar;
import com.googlecode.likesql.constant.SqlConstant;
import com.googlecode.likesql.converter.Converter;
import com.googlecode.likesql.converter.ConverterTable;
import com.googlecode.likesql.utils.StringUtils;

/**
 * The SerialSqlContext class represents a SQL context can be serial modified.
 * @author Johny Huang
 *
 */
public class SerialSqlContext implements SqlContext{

	/**
	 * A StringBuffer for building SQL.
	 */
	private StringBuffer sql;
	
	/**
	 * A StringBuffer for building SQL template.
	 */
	private StringBuffer template;
	
	/**
	 * A List containing the values.
	 */
	private List valueList;
	
	/**
	 * Construct a new instance.
	 */
	public SerialSqlContext(){
		sql=new StringBuffer();
		template=new StringBuffer();
		valueList=new ArrayList();
	}
	
	/**
	 * Appends the string representation of the <code>Object</code> 
     * argument to this context. The string presents in both SQL and SQL template.
     * <p>
     * The argument is converted to a string as if by the method 
     * <code>String.valueOf</code>, and the characters of that 
     * string are then appended to this context.
     * 
	 * @param obj an <code>Object</code> 
	 * @return a reference to this <code>SerialSqlContext</code>
	 */
	public SerialSqlContext append(Object obj){
		sql.append(obj);
		template.append(obj);	
		return this;
	}
		
	/**
	 * Appends the character to this context. This character presents in both SQL and SQL template.
     * 
     * 
	 * @param ch an Character
	 * @return a reference to this <code>SerialSqlContext</code>
	 */
	public SerialSqlContext append(char ch){
		sql.append(ch);
		template.append(ch);
		return this;
	}
	
	/**
	 * Appends the format result according to the pattern into context. 
	 * The first character {@link SqlConstant#CHAR_FOR_REPLACE} in pattern is replaced by the string representation of <i>value</i>.
     * The <i>value</i> argument is converted to a SQL string and SQL template according to its type, as if by the mapping converter implements
     * <code>{@link Converter}</code>.
     * 
	 * @param pattern
	 * @param value
	 * @return
	 * 
	 * @see Converter
	 * @see ConverterTable#getConverter(String)
	 */
	public SerialSqlContext append(String pattern, Object value){
		Converter cvt=getConverter(value);
		sql.append(format(pattern,cvt.toSql(value)));
		template.append(format(pattern,cvt.toTemplate(value)));
		valueList.addAll(cvt.toValueList(value));
		return this;
	}
	
	/**
	 * Appends a space character and the format result according to the pattern into context. 
	 * 
	 * @param pattern
	 * @param value
	 * @return
	 * 
	 * @see #append(String, Object)
	 */
	public SerialSqlContext appendSP(String pattern, Object value){
		appendSP().append(pattern, value);
		return this;
	}
	
	/**
	 * Appends a space character to this context. This character presents in both SQL and SQL template.
     * 
     * 
	 * @return a reference to this <code>SerialSqlContext</code>
	 */
	public SerialSqlContext appendSP(){
		return append(SqlChar.SP);
	}
	
	/**
	 * Appends a space character and the string representation of the <code>Object</code> 
     * argument to this context. The string presents in both SQL and SQL template.
     * <p>
     * The argument is converted to a string as if by the method 
     * <code>String.valueOf</code>, and the characters of that 
     * string are then appended to this context.
     * 
	 * @param obj an <code>Object</code> 
	 * @return a reference to this <code>SerialSqlContext</code>
	 */
	public SerialSqlContext appendSP(Object obj){
		return append(SqlChar.SP).append(obj);
	}
	
	public String getSql() {
		return sql.toString();
	}

	public String getTemplate() {
		return template.toString();
	}
	
	public PreparedStatement getPreStatement(Connection con) throws SQLException{
		if(con==null){
			return null;
		}
		PreparedStatement ps=con.prepareStatement(template.toString());
		int count=1;
		while(valueList.size()>=count){
			ps.setObject(count, valueList.get(count-1));
			count++;
		}
		return ps;
	}
	
	protected String format(String pattern, String value) {
		return StringUtils.replaceFirst(pattern, SqlConstant.CHAR_FOR_REPLACE, value);
	}
	
	protected Converter getConverter(Object value) {
		String clazzName = null;
		if (value != null) {
			clazzName = value.getClass().getName();
		}
		return ConverterTable.getInstance().getConverter(clazzName);
	}

	public List getValueList() {
		return valueList;
	}

}
